/** Implementation of Parser Class.
 * @file 
 * @author Jeremy Schultz
 */
#include "Parser.h"

using namespace std;

//-----------------------------------------------------------------------------
// Some defines to make the code more readable
#define FOLLOW(x)	follow->Set(PLset::x)
#define FIRST(x)	first->Set(PLset::x)

//-----------------------------------------------------------------------------
// Constructor
Parser::Parser(Scanner* _scanner, Administration* _admin, SymbolTable* _symTable)  {
	first = new First();	
	follow = new Follow();	
	scanner = _scanner;
	admin = _admin;
	symTable = _symTable;

	curToken = scanner->nextToken();
	tabStop = "  ";
}

//-----------------------------------------------------------------------------
// Start the parser
void Parser::start() {
	PLset* syncSet = new PLset(); 
	syncSet->add(new Token(Token::EMPTY) );

	program(syncSet);
}

/*****************************************************************************
 * Match methods 
 *****************************************************************************/
//-----------------------------------------------------------------------------
// match a Keyword
void Parser::match(Word* terminal, PLset* syncSet)	{
	if  (curToken->Type() != terminal->Type() )	{
		// Error: Token Types do not match
		admin->errorMatch(terminal->Lex(), curToken, scanner->LineNum() );
		sync(syncSet);
		return;

	} else if ( (WORD(curToken))->Lex() != terminal->Lex())	{
		// Error: Words do not match
		admin->errorMatch(terminal->Lex(), curToken, scanner->LineNum() );
		sync(syncSet);
		return;
	}

	//cout << indent <<"MATCH " << terminal->Lex() << endl;
	curToken = scanner->nextToken();
}

//-----------------------------------------------------------------------------
// match an ID
void Parser::matchID(Word* w, PLset* syncSet)	{
	if  (curToken->Type() != Token::ID )	{
		// Error: Token Type is not ID
		admin->errorMatch(Token::ID, curToken, scanner->LineNum() );
		sync(syncSet);
		return;
	}
	
	if (w != NULL)	{
		w->setLex( ( WORD(curToken) )->Lex() );
	}

	//cout << indent << "MATCH ID: " << ( WORD(curToken) )->Lex() << endl;
	curToken = scanner->nextToken();
}

//-----------------------------------------------------------------------------
// match a Symbol
void Parser::match(Token::TokenType termType, PLset* syncSet)	{

	if  (curToken->Type() != termType )	{
		// indent Error: Token Types do not match
		admin->errorMatch(termType, curToken, scanner->LineNum() );
		sync(syncSet);
		return;
	}
	
	//cout << indent << "MATCH Symbol: " << Token::toString(termType) << endl;
	curToken = scanner->nextToken();
}




/*****************************************************************************
 * Sync methods 
 *****************************************************************************/
//-----------------------------------------------------------------------------
// Sync Input 
void Parser::sync(PLset* syncSet)	{
	while(curToken->Type() != Token::EMPTY && !syncSet->has(curToken) )	{
		curToken = scanner->nextToken();	
	}
}


/*****************************************************************************
 * Misc/Helper methods 
 *****************************************************************************/
//-----------------------------------------------------------------------------
// Create a new Label
int Parser::newLabel() {
	static int l = 1;

	if (l > MAX_LABELS)		{
		admin->errorLabelMax(MAX_LABELS, scanner->LineNum() );
		return 0;
	}

	return l++;
}

//-----------------------------------------------------------------------------
// Get the Numeric Value from Word
int Parser::getNumericValue(Word* w) {

	// Error word is NULL
	// This should to happen, but just in case
	if (w == NULL) {
		return 0;
	}

	// Find w in symTable
	if (w->Data() == NULL ) {
		Word* tmp = symTable->find( w->Lex() );

		if (tmp == NULL) {
			admin->errorUndefined(w->Lex(), scanner->LineNum());

			//Add the ID to avoid similar errors
			symTable->insert(w);
			w->setData(WordData::ERROR);
			return 0;
		}

		*w = *(  symTable->find( w->Lex() )  );
		return getNumericValue(w);
	}

	// ID
	if (( w->Data() )->Type() == WordData::ID) {
		*w = *(  symTable->find( DATAID(w->Data() )->Id() )  );
		return getNumericValue(w);
	}

	// Number
	if (( w->Data() )->Type() == WordData::NUMBER) {
		return (DATANUMBER( w->Data() ))->Value();
	}

	// Bool
	if ( ( w->Data() )->Type() == WordData::BOOL) {
		return (DATABOOL( w->Data() ))->Value();
	}

	// Error
	admin->errorInvalidValue(scanner->LineNum() );
	return 0;
}



/*****************************************************************************
 * Right-hand side of PL BNF
 *****************************************************************************/
//-----------------------------------------------------------------------------
// PROGRAM
void Parser::program(PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "PROGRAM" << endl;
		indent+=tabStop;
	#endif
	int lVarLength, lAddress;
	lVarLength = newLabel();
	lAddress = newLabel();

	admin->emit("PROG", lVarLength, lAddress);
	block(lVarLength, lAddress, new PLset(syncSet, FOLLOW(BLOCK)) );

	match(Token::PERIOD, syncSet);
	admin->emit("ENDPROG");

	sync(syncSet);
	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// BLOCK
void Parser::block(int lVarLength, int lAddress, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "BLOCK" << endl;
		indent+=tabStop;
	#endif
	int varLength = 0;

	if (!symTable->newBlock() ){
		admin->errorMaxBlocks( scanner->LineNum() );
		return;
	}

	match(new Word("begin", Token::KEYWORD) , new PLset(syncSet, FIRST(DEFINITION_PART) ));

	definitionPart(varLength, new PLset(syncSet, FOLLOW(DEFINITION_PART)) );
	admin->emit("DEFARG", lVarLength, varLength);

	admin->emit("DEFADDR", lAddress);
	statementPart( new PLset(syncSet, FOLLOW(STATEMENT_PART)) );

	match(new Word("end", Token::KEYWORD), syncSet);

	//symTable->dumpTable();
	symTable->destroyBlock();

	sync(syncSet);
	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// DEFINITION_PART
void Parser::definitionPart(int &varLength, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "DEFINITION_PART" << endl;
		indent+=tabStop;
	#endif
		
	// if the current Token is in the not in the first set, then skip
	if (!FIRST(DEFINITION_PART)->has(curToken ) )	{
		indent.erase(0, tabStop.length() );
		return;
	}

	definition(varLength, new PLset(syncSet, FOLLOW(DEFINITION)) );
	match(Token::SEMI_COLON, new PLset(syncSet, FIRST(DEFINITION_PART) ));
	definitionPart(varLength, new PLset(syncSet, FOLLOW(DEFINITION_PART)) );

	sync(syncSet);
	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// DEFINITION
void Parser::definition(int &varLength, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "DEFINITION" << endl;
		indent+=tabStop;
	#endif

	if (FIRST(CONSTANT_DEFINITION)->has(curToken) )	{
		constantDefinition( new PLset(syncSet, FOLLOW(CONSTANT_DEFINITION)) );

	}else if (FIRST(VARIABLE_DEFINITION)->has(curToken) )	{
		variableDefinition(varLength, new PLset(syncSet, FOLLOW(VARIABLE_DEFINITION)) );

	}else if (FIRST(PROCEDURE_DEFINITION)->has(curToken) )	{
		procedureDefinition( new PLset(syncSet, FOLLOW(PROCEDURE_DEFINITION)) );

	}else	{
		admin->errorDefinition(scanner->LineNum() );
		sync(syncSet);
		indent.erase(0, tabStop.length() );
		return;
	}

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// CONSTANT_DEFINITION
void Parser::constantDefinition(PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "CONSTANT_DEFINITION" << endl;
		indent+=tabStop;
	#endif
	Word* w = new Word();

	//match(new Word("const", Token::KEYWORD) );
	match(new Word("const", Token::KEYWORD), new PLset(syncSet, FIRST(CONSTANT_NAME) ));
	constantName(w, new PLset(syncSet, FOLLOW(CONSTANT_NAME)) );
	match(Token::EQUAL, new PLset(syncSet, FIRST(CONSTANT) ));
	constant(w, new PLset(syncSet, FOLLOW(CONSTANT)) );

	// Set the Data to be a constant
	( w->Data() )->setConstant(true);
	if(!symTable->insert(w) )	{
		admin->errorInsert(w->Lex(), scanner->LineNum() );
	}

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// VARIABLE_DEFINITION
void Parser::variableDefinition(int &varLength, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "VARIABLE_DEFINITION" << endl;
		indent+=tabStop;
	#endif
	Word* w = new Word();

	typeSymbol(w, new PLset(syncSet, FOLLOW(TYPE_SYMBOL)) );
	variableDefinition1(w, varLength,  new PLset(syncSet, FOLLOW(VARIABLE_DEFINITION1)) );

	indent.erase(0,tabStop.length());
}

//-----------------------------------------------------------------------------
// VARIABLE_DEFINITION1
void Parser::variableDefinition1(Word* wtype, int &varLength, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "VARIABLE_DEFINITION1" << endl;
		indent+=tabStop;
	#endif
	vector<Word*> wList;
	WordData::DataType type = ( wtype->Data() )->Type();

	if (curToken->Type() == Token::KEYWORD && WORD(curToken)->Lex() == "array")	{
		Word* wSize = new Word();
		int size;

		match(new Word("array", Token::KEYWORD), new PLset(syncSet, FIRST(VARIABLE_LIST) ));
		variableList(wList, new PLset(syncSet, FOLLOW(VARIABLE_LIST)) );
		match(Token::LEFT_BRACKET, new PLset(syncSet, FIRST(CONSTANT) ));
		constant(wSize, new PLset(syncSet, FOLLOW(CONSTANT)) );
		match(Token::RIGHT_BRACKET, syncSet);

		// Get size and verify it is a constant
		size = getNumericValue(wSize);
		
		for(vector<Word*>::iterator w = wList.begin(); w != wList.end(); w++ ) {
			varLength+=size;
			(*w)->setOffset( symTable->nextOffset() );
			(*w)->setData(type, size);
			if(!symTable->insert(*w) )	{
				admin->errorInsert( (*w)->Lex(), scanner->LineNum() );
			}
		}

	}else if (FIRST(VARIABLE_LIST)->has(curToken) )	{
		variableList(wList, new PLset(syncSet, FOLLOW(VARIABLE_LIST)) );
	
		for(vector<Word*>::iterator w = wList.begin(); w != wList.end(); w++ ) {
			varLength++;
			(*w)->setOffset( symTable->nextOffset() );
			(*w)->setData(type);
			if(!symTable->insert(*w) )	{
				admin->errorInsert( (*w)->Lex(), scanner->LineNum() );
			}
		}


	}else	{
		admin->errorVariableDef(scanner->LineNum());
		sync(syncSet);
		indent.erase(0, tabStop.length() );
		return;
	}

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// TYPE_SYMBOL
void Parser::typeSymbol(Word* w, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "TYPE_SYMBOL" << endl;
		indent+=tabStop;
	#endif

	
	if (curToken->Type() == Token::KEYWORD && WORD(curToken)->Lex() == "integer")	{
		if (w != NULL) {
			w->setData(WordData::NUMBER);
		}
		match(new Word("integer", Token::KEYWORD) , syncSet);

	}else if (curToken->Type() == Token::KEYWORD && WORD(curToken)->Lex() == "Boolean")	{
		if (w != NULL) {
			w->setData(WordData::BOOL);
		}
		match(new Word("Boolean", Token::KEYWORD) , syncSet);

	}else	{
		admin->errorInvalidValue(scanner->LineNum() );
		sync(syncSet);
	}

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// VARIABLE_LIST
void Parser::variableList(vector<Word*> &wList, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "VARIABLE_LIST" << endl;
		indent+=tabStop;
	#endif

	wList.push_back(new Word);
	variableName(wList.back() , new PLset(syncSet, FOLLOW(VARIABLE_NAME)) );
	variableName1(wList, new PLset(syncSet, FOLLOW(VARIABLE_NAME1)) );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// VARIABLE_NAME1
void Parser::variableName1(vector<Word*> &wList, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "VARIABLE_NAME1" << endl;
		indent+=tabStop;
	#endif

	// if the current Token is not in the first set, then skip
	if (!FIRST(VARIABLE_NAME1)->has(curToken) )	{
		indent.erase(0, tabStop.length() );
		return;
	}

	match(Token::COMMA, new PLset(syncSet, FIRST(VARIABLE_NAME) ));
	wList.push_back(new Word);
	variableName(wList.back(), new PLset(syncSet, FOLLOW(VARIABLE_NAME)) );
	variableName1(wList, new PLset(syncSet, FOLLOW(VARIABLE_NAME1)) );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// PROCEDURE_DEFINITION
void Parser::procedureDefinition(PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "PROCEDURE_DEFINITION" << endl;
		indent+=tabStop;
	#endif
	int lVarLength, lAddress, lProcAddress;

	Word* w = new Word();
	lVarLength = newLabel();
	lAddress = newLabel();

	match(new Word("proc", Token::KEYWORD) , new PLset(syncSet, FIRST(PROCEDURE_NAME) ));

	procedureName(w, new PLset(syncSet, FOLLOW(PROCEDURE_NAME)) );
	lProcAddress = newLabel();
	admin->emit("DEFADDR", lProcAddress);
	DATAPROCEDURE( w->Data() )->setAddressLabel( lProcAddress );
	if(!symTable->insert(w) )	{
		admin->errorInsert(w->Lex(), scanner->LineNum() );
	}

	admin->emit("PROC", lVarLength, lAddress);
	block(lVarLength, lAddress, syncSet);
	admin->emit("ENDPROC");

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// STATEMENT_PART
void Parser::statementPart(PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "STATEMENT_PART" << endl;
		indent+=tabStop;
	#endif

	// if the current Token is not in the first set, then skip
	if (!FIRST(STATEMENT_PART)->has(curToken) )	{
		indent.erase(0, tabStop.length() );
		return;
	}

	statement( new PLset(syncSet, FOLLOW(STATEMENT)) );
	match(Token::SEMI_COLON, new PLset(syncSet, FIRST(STATEMENT_PART) ));
	statementPart( new PLset(syncSet, FOLLOW(STATEMENT_PART)) );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// STATEMENT
void Parser::statement(PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "STATEMENT" << endl;
		indent+=tabStop;
	#endif

	if (FIRST(EMPTY_STATEMENT)->has(curToken ) )	{
		emptyStatement( new PLset(syncSet, FOLLOW(EMPTY_STATEMENT)) );

	}else if (FIRST(READ_STATEMENT)->has(curToken ) )	{
		readStatement( new PLset(syncSet, FOLLOW(READ_STATEMENT)) );

	}else if (FIRST(WRITE_STATEMENT)->has(curToken ) )	{
		writeStatement( new PLset(syncSet, FOLLOW(WRITE_STATEMENT)) );

	}else if (FIRST(ASSIGNMENT_STATEMENT)->has(curToken ) )	{
		assignmentStatement( new PLset(syncSet, FOLLOW(ASSIGNMENT_STATEMENT)) );

	}else if (FIRST(PROCEDURE_STATEMENT)->has(curToken ) )	{
		procedureStatement( new PLset(syncSet, FOLLOW(PROCEDURE_STATEMENT)) );

	}else if (FIRST(IF_STATEMENT)->has(curToken ) )	{
		ifStatement( new PLset(syncSet, FOLLOW(IF_STATEMENT)) );

	}else if (FIRST(DO_STATEMENT)->has(curToken ) )	{
		doStatement( new PLset(syncSet, FOLLOW(DO_STATEMENT)) );

			
	}else	{
		admin->errorStatement(scanner->LineNum() );
		sync(syncSet);
		indent.erase(0, tabStop.length() );
		return;
	}

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// EMPTY_STATEMENT
void Parser::emptyStatement(PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "EMPTY_STATEMENT" << endl;
		indent+=tabStop;
	#endif

	match(new Word("skip", Token::KEYWORD) , syncSet);

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// READ_STATEMENT
void Parser::readStatement(PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "READ_STATEMENT" << endl;
		indent+=tabStop;
	#endif
	vector<Word*> wList;
	wList.clear();

	match(new Word("read", Token::KEYWORD), new PLset(syncSet, FIRST(VARIABLE_ACCESS_LIST) ));
	variableAccessList(wList,  new PLset(syncSet, FOLLOW(VARIABLE_ACCESS_LIST)) );
	admin->emit("READ", wList.size() );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// VARIABLE_ACCESS_LIST
void Parser::variableAccessList(vector<Word*> &wList, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "VARIABLE_ACCESS_LIST" << endl;
		indent+=tabStop;
	#endif

	wList.push_back(new Word() );
	variableAccess(wList.back(), new PLset(syncSet, FOLLOW(VARIABLE_ACCESS)) );
	variableAccess1(wList, new PLset(syncSet, FOLLOW(VARIABLE_ACCESS1)) );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// VARIABLE_ACCESS1
void Parser::variableAccess1(vector<Word*> &wList, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "VARIABLE_ACCESS1" << endl;
		indent+=tabStop;
	#endif

	// if the current Token is not in the first set, then skip
	if (!FIRST(VARIABLE_ACCESS1)->has(curToken) )	{
		indent.erase(0, tabStop.length() );
		return;
	}
	match(Token::COMMA, new PLset(syncSet, FIRST(VARIABLE_ACCESS) ));
	wList.push_back(new Word() );
	variableAccess(wList.back(), new PLset(syncSet, FOLLOW(VARIABLE_ACCESS)) );
	variableAccess1(wList,  new PLset(syncSet, FOLLOW(VARIABLE_ACCESS1)) );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// WRITE_STATEMENT
void Parser::writeStatement(PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "WRITE_STATEMENT" << endl;
		indent+=tabStop;
	#endif
	vector<WordData::DataType> dTypes;
	
	match(new Word("write", Token::KEYWORD) , new PLset(syncSet, FIRST(EXPRESSION_LIST) ));
	expressionList(dTypes, new PLset(syncSet, FOLLOW(EXPRESSION_LIST)) );
	admin->emit("WRITE", dTypes.size() );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// EXPRESSION_LIST
void Parser::expressionList(vector<WordData::DataType> &dTypes, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "EXPRESSION_LIST" << endl;
		indent+=tabStop;
	#endif
	WordData::DataType dType = WordData::EMPTY;

	expression(dType, new PLset(syncSet, FOLLOW(EXPRESSION)) );
	dTypes.push_back(dType);
	expression1(dTypes, new PLset(syncSet, FOLLOW(EXPRESSION1)) );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// EXPRESSION1
void Parser::expression1(vector<WordData::DataType> &dTypes, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "EXPRESSION1" << endl;
		indent+=tabStop;
	#endif
		WordData::DataType dType = WordData::EMPTY;

	// if the current Token is not in the first set, then skip
	if (!FIRST(EXPRESSION1)->has(curToken ) )	{
		indent.erase(0, tabStop.length() );
		return;
	}
	match(Token::COMMA, new PLset(syncSet, FIRST(EXPRESSION) ));
	expression(dType, new PLset(syncSet, FOLLOW(EXPRESSION)) );
	dTypes.push_back(dType);
	expression1(dTypes,  new PLset(syncSet, FOLLOW(EXPRESSION1)) );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// ASSIGNMENT_STATEMENT
void Parser::assignmentStatement(PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "ASSIGNMENT_STATEMENT" << endl;
		indent+=tabStop;
	#endif
	vector<Word*> vList;
	vector<WordData::DataType> dTypes;

	variableAccessList(vList, new PLset(syncSet, FOLLOW(VARIABLE_ACCESS_LIST)) );
	match(Token::ASSIGN, new PLset(syncSet, FIRST(EXPRESSION_LIST) ));
	expressionList(dTypes, new PLset(syncSet, FOLLOW(EXPRESSION_LIST)) );

	//Compare Types
	for(unsigned int i=0; i < vList.size() && i < dTypes.size(); i++) {
		if ( ( vList[i]->Data() )->Type() != dTypes[i]) {
			/// HERE 
			admin->errorMismatchedTypes(scanner->LineNum() );
		}
	}

	// Check if assignment statement is balanced
	if (vList.size() != dTypes.size() ) {
		admin->errorUnbalanced(scanner->LineNum() );
	}

	admin->emit("ASSIGN", vList.size() );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// PROCEDURE_STATEMENT
void Parser::procedureStatement(PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "PROCEDURE_STATEMENT" << endl;
		indent+=tabStop;
	#endif
	Word* w = new Word();
	Word* found;

	match(new Word("call", Token::KEYWORD) , new PLset(syncSet, FIRST(PROCEDURE_NAME) ));
	procedureName(w, new PLset(syncSet, FOLLOW(PROCEDURE_NAME)) );

	//Check if procedure exists
	if ( (found = symTable->find(w->Lex()) ) == NULL) {
		//TODO
		admin->errorUndefined(w->Lex(), scanner->LineNum() );

		//Add the value to avoid similar errors
		symTable->insert(w);
		w->setData(WordData::ERROR);
	
	// Check if word is defined as a Procedure
	}else if ( ( found->Data() )->Type() != WordData::PROCEDURE) {
		admin->errorNotProcedure(found->Lex(), scanner->LineNum() );

	// Call the Procedure 
	}else {
		admin->emit("CALL", symTable->blockLevel() - found->Level(), DATAPROCEDURE(found->Data() )->AddressLabel() );
	}


	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// IF_STATEMENT
void Parser::ifStatement(PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "IF_STATEMENT" << endl;
		indent+=tabStop;
	#endif
	int lEnd, lNext;

	lEnd = newLabel();
	lNext = newLabel();

	match(new Word("if", Token::KEYWORD), new PLset(syncSet, FIRST(GUARDED_COMMAND_LIST) ));
	guardedCommandList(lEnd, lNext, new PLset(syncSet, FOLLOW(GUARDED_COMMAND_LIST)) );
	admin->emit("DEFADDR", lNext);

	match(new Word("fi", Token::KEYWORD) , syncSet);
	admin->emit("FI", scanner->LineNum() );
	admin->emit("DEFADDR", lEnd);

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// DO_STATEMENT
void Parser::doStatement(PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "DO_STATEMENT" << endl;
		indent+=tabStop;
	#endif
	int lStart, lEnd;

	lStart = newLabel();
	lEnd = newLabel();

	match(new Word("do", Token::KEYWORD), new PLset(syncSet, FIRST(GUARDED_COMMAND_LIST) ));

	admin->emit("DEFADDR", lStart);
	guardedCommandList(lStart, lEnd, new PLset(syncSet, FOLLOW(GUARDED_COMMAND_LIST)) );
	match(new Word("od", Token::KEYWORD) , syncSet);

	admin->emit("FI", scanner->LineNum() );
	admin->emit("DEFADDR", lEnd);

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// GUARDED_COMMAND_LIST
void Parser::guardedCommandList(int lGoto, int &lNext, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "GUARDED_COMMAND_LIST" << endl;
		indent+=tabStop;
	#endif

	guardedCommand(lNext, new PLset(syncSet, FOLLOW(GUARDED_COMMAND)) );
	guardedCommand1(lGoto, lNext, new PLset(syncSet, FOLLOW(GUARDED_COMMAND1)) );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// GUARDED_COMMAND1
void Parser::guardedCommand1(int lGoto, int &lNext, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "GUARDED_COMMAND1" << endl;
		indent+=tabStop;
	#endif

	// Do: goto start of loop
	// If: goto end of If
	admin->emit("BAR", lGoto);

	// if the current Token is not in the first set, then skip
	if (!FIRST(GUARDED_COMMAND1)->has(curToken ) )	{
		indent.erase(0, tabStop.length() );
		return;
	}

	match(Token::GUARD_SEPERATOR, new PLset(syncSet, FIRST(GUARDED_COMMAND) ));
	admin->emit("DEFADDR", lNext);
	lNext = newLabel();

	guardedCommand(lNext, new PLset(syncSet, FOLLOW(GUARDED_COMMAND)) );
	guardedCommand1(lGoto, lNext, new PLset(syncSet, FOLLOW(GUARDED_COMMAND1)) );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// GUARDED_COMMAND
void Parser::guardedCommand(int &lNext, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "GUARDED_COMMAND" << endl;
		indent+=tabStop;
	#endif
	WordData::DataType dType = WordData::EMPTY;

	expression(dType, new PLset(syncSet, FOLLOW(EXPRESSION)) );
	// Expecting Boolean
	if (dType != WordData::BOOL) {
		admin->errorNotBoolean(scanner->LineNum() );
	}
	match(Token::ARROW, new PLset(syncSet, FIRST(STATEMENT_PART) ));

	// If false goto next statement
	admin->emit("ARROW", lNext);

	statementPart( new PLset(syncSet, FOLLOW(STATEMENT_PART)) );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// EXPRESSION
void Parser::expression(WordData::DataType &dType, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "EXPRESSION" << endl;
		indent+=tabStop;
	#endif
	
	primaryExpression(dType, new PLset(syncSet, FOLLOW(PRIMARY_EXPRESSION)) );
	primaryExpression1(dType, new PLset(syncSet, FOLLOW(PRIMARY_EXPRESSION1)) );


	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// PRIMARY_EXPRESSION1
void Parser::primaryExpression1(WordData::DataType &dType, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "PRIMARY_EXPRESSION1" << endl;
		indent+=tabStop;
	#endif
	string oper;

	// if the current Token is not in the first set, then skip
	if (!FIRST(PRIMARY_EXPRESSION1)->has(curToken ) )	{
		indent.erase(0, tabStop.length() );
		return;
	}

	// dType should be Bool, since we are using primaryOperator
	if (dType != WordData::BOOL) {
		admin->errorNotBoolean(scanner->LineNum() );
	}

	primaryOperator(oper, new PLset(syncSet, FOLLOW(PRIMARY_OPERATOR)) );

	//Reset to empty
	dType = WordData::EMPTY;
	primaryExpression(dType, new PLset(syncSet, FOLLOW(PRIMARY_EXPRESSION)) );
	// dType should be Bool, since we are using primaryOperator
	if (dType != WordData::BOOL) {
		admin->errorNotBoolean(scanner->LineNum() );
	}
	
	admin->emit(oper);
	
	primaryExpression1(dType, new PLset(syncSet, FOLLOW(PRIMARY_EXPRESSION1)) );

	// Primary Operator means type will be a Boolean
	dType = WordData::BOOL;

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// PRIMARY_OPERATOR
void Parser::primaryOperator(string &oper, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "PRIMARY_OPERATOR" << endl;
		indent+=tabStop;
	#endif
	
	if (curToken->Type() == Token::AND)	{
		match(Token::AND, syncSet);
		oper = "AND";

	}else if (curToken->Type() == Token::OR)	{
		match(Token::OR, syncSet);
		oper = "OR";

	}else	{
		admin->errorInvalidPrimOper(scanner->LineNum() );
		sync(syncSet);
		indent.erase(0, tabStop.length() );
		return;
	}

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// PRIMARY_EXPRESSION
void Parser::primaryExpression(WordData::DataType &dType, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "PRIMARY_EXPRESSION" << endl;
		indent+=tabStop;
	#endif
	
	simpleExpression(dType, new PLset(syncSet, FOLLOW(SIMPLE_EXPRESSION)) );
	primaryExpression11(dType, new PLset(syncSet, FOLLOW(PRIMARY_EXPRESSION11)) );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// PRIMARY_EXPRESSION11
void Parser::primaryExpression11(WordData::DataType &dType, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "PRIMARY_EXPRESSION11" << endl;
		indent+=tabStop;
	#endif
	string relOp;

	// if the current Token is not in the first set, then skip
	if (!FIRST(PRIMARY_EXPRESSION11)->has(curToken ) )	{
		indent.erase(0, tabStop.length() );
		return;
	}


	relationalOperator(relOp, new PLset(syncSet, FOLLOW(RELATIONAL_OPERATOR)) );

	// Reset to empty
	dType = WordData::EMPTY;
	simpleExpression(dType, new PLset(syncSet, FOLLOW(SIMPLE_EXPRESSION)) );

	admin->emit(relOp);
	
	// Relation Operator means type will be a Boolean
	dType = WordData::BOOL;

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// RELATIONAL_OPERATOR
void Parser::relationalOperator(string &relOp, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "RELATIONAL_OPERATOR" << endl;
		indent+=tabStop;
	#endif

	if (curToken->Type() == Token::LESS_THAN)	{
		match(Token::LESS_THAN, syncSet);
		relOp = "LESS";

	}else if (curToken->Type() == Token::EQUAL)	{
		match(Token::EQUAL, syncSet);
		relOp = "EQUAL";

	}else if (curToken->Type() == Token::GREATER_THAN)	{
		match(Token::GREATER_THAN, syncSet);
		relOp = "GREATER";

	}else	{
		admin->errorInvalidRelatOper(scanner->LineNum() );
		sync(syncSet);
		indent.erase(0, tabStop.length() );
		return;
	}


	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// SIMPLE_EXPRESSION
void Parser::simpleExpression(WordData::DataType &dType, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "SIMPLE_EXPRESSION" << endl;
		indent+=tabStop;
	#endif
	string neg;

	negativeSymbol(neg, dType, new PLset(syncSet, FOLLOW(NEGATIVE_SYMBOL)) );
	term(dType, new PLset(syncSet, FOLLOW(TERM)) );
	if (!neg.empty() ) {
		admin->emit(neg);
	}
	simpleExpression1(dType, new PLset(syncSet, FOLLOW(SIMPLE_EXPRESSION1)) );


	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// NEGATIVE_SYMBOL
void Parser::negativeSymbol(string &neg, WordData::DataType &dType, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "NEGATIVE_SYMBOL" << endl;
		indent+=tabStop;
	#endif
	

	// if the current token is not in the first set, then skip
	if (!FIRST(NEGATIVE_SYMBOL)->has(curToken ) )	{
		indent.erase(0, tabStop.length() );
		return;
	}
	match(Token::MINUS, syncSet);
	neg = "MINUS";


	// Negative means type will be a Integer
	dType = WordData::NUMBER;

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// SIMPLE_EXPRESSION1
void Parser::simpleExpression1(WordData::DataType &dType, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "SIMPLE_EXPRESSION1" << endl;
		indent+=tabStop;
	#endif
	string oper;

	// if the current token is not in the first set, then skip
	if (!FIRST(SIMPLE_EXPRESSION1)->has(curToken ) )	{
		indent.erase(0, tabStop.length() );
		return;
	}

	// dType should be Integer, since we are using adding Operator
	if (dType != WordData::NUMBER) {
		admin->errorNotInteger(scanner->LineNum() );
	}
	
	addingOperator(oper, new PLset(syncSet, FOLLOW(ADDING_OPERATOR)) );

	//Reset to empty
	dType = WordData::EMPTY;
	term(dType, new PLset(syncSet, FOLLOW(TERM)) );

	// dType should be Integer, since we are using adding Operator
	if (dType != WordData::NUMBER) {
		admin->errorNotInteger(scanner->LineNum() );
	}

	admin->emit(oper);
	
	simpleExpression1(dType, new PLset(syncSet, FOLLOW(SIMPLE_EXPRESSION1)) );

	// MulitplyingOperator means result will be a Integer
	dType = WordData::NUMBER;

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// ADDING_OPERATOR
void Parser::addingOperator(string &oper, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "ADDING_OPERATOR" << endl;
		indent+=tabStop;
	#endif

	if (curToken->Type() == Token::ADD)	{
		match(Token::ADD, syncSet);
		oper = "ADD";

	}else if (curToken->Type() == Token::MINUS)	{
		match(Token::MINUS, syncSet);
		oper = "SUBTRACT";

	}else	{
		admin->errorInvalidAddOper(scanner->LineNum() );
		sync(syncSet);
		indent.erase(0, tabStop.length() );
		return;
	}


	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// TERM
void Parser::term(WordData::DataType &dType, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "TERM" << endl;
		indent+=tabStop;
	#endif

	factor(dType, new PLset(syncSet, FOLLOW(FACTOR)) );
	term1(dType, new PLset(syncSet, FOLLOW(TERM1)) );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// TERM1
void Parser::term1(WordData::DataType &dType, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "TERM1" << endl;
		indent+=tabStop;
	#endif
	string oper;

	// if the curToken is not in the first set, then skip
	if (!FIRST(TERM1)->has(curToken ) )	{
		indent.erase(0, tabStop.length() );
		return;
	}

	// dType should be Integer, since we are using multiplying Operator
	if (dType != WordData::NUMBER) {
		admin->errorNotInteger(scanner->LineNum() );
	}

	multiplyingOperator(oper, new PLset(syncSet, FOLLOW(MULTIPLYING_OPERATOR)) );
	// Reset dType
	dType = WordData::EMPTY;
	factor(dType, new PLset(syncSet, FOLLOW(FACTOR)) );

	// dType should be Integer, since we are using multiplying Operator
	if (dType != WordData::NUMBER) {
		admin->errorNotInteger(scanner->LineNum() );
	}

	admin->emit(oper);

	term1(dType, new PLset(syncSet, FOLLOW(TERM1)) );

	// MulitplyingOperator means result will be a Integer
	dType = WordData::NUMBER;

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// MULTIPLYING_OPERATOR
void Parser::multiplyingOperator(string &oper, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "MULTIPLYING_OPERATOR" << endl;
		indent+=tabStop;
	#endif

	if (curToken->Type() == Token::TIMES)	{
		match(Token::TIMES, syncSet);
		oper = "MULTIPLY";

	}else if (curToken->Type() == Token::MOD)	{
		match(Token::MOD, syncSet);
		oper = "MODULO";

	}else if (curToken->Type() == Token::DIV)	{
		match(Token::DIV, syncSet);
		oper = "DIVIDE";

	}else	{
		admin->errorInvalidMultiOper(scanner->LineNum() );
		sync(syncSet);
		indent.erase(0, tabStop.length() );
		return;
	}



	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// FACTOR
void Parser::factor(WordData::DataType &dType, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "FACTOR" << endl;
		indent+=tabStop;
	#endif

	// Cannot really tell between an ID for variable access and an ID for 
	//  a constant name. 
	// Constant name is the same as variableAccess without an array index
	//  So there is no need for calling constant to get constant name
	if ( FIRST(VARIABLE_ACCESS)->has(curToken ) ) {
		Word* w = new Word();

		variableAccess(w, new PLset(syncSet, FOLLOW(VARIABLE_ACCESS)) );

		// dType will not be empty if previous section has decided
		//  what the type should be
		if (dType == WordData::EMPTY) {
			dType = ( w->Data() )->Type();

		}else if (dType != ( w->Data() )->Type() ) {
			//TODO mis match error
			admin->errorMismatchedTypes(scanner->LineNum() );
			dType = WordData::ERROR;
		}

		// Only admin->emit value if variable access is not a constant
		if ( !( w->Data() )->Constant() ) { 
			admin->emit("VALUE");
		}

		

	// Handles constants, but not constant_names (data type ID)
	}else if (FIRST(CONSTANT)->has(curToken ) )	{
		Word* w = new Word();
		constant(w, new PLset(syncSet, FOLLOW(CONSTANT)) );
		dType = ( w->Data() )->Type();

		admin->emit("CONSTANT", getNumericValue(w) );

	}else if (curToken->Type() == Token::LEFT_PARATHESIS)	{
		match(Token::LEFT_PARATHESIS, new PLset(syncSet, FIRST(EXPRESSION) ));
		expression(dType, new PLset(syncSet, FOLLOW(EXPRESSION)) );
		match(Token::RIGHT_PARATHESIS, syncSet);

	}else if (curToken->Type() == Token::NOT)	{
		match(Token::NOT, new PLset(syncSet, FIRST(FACTOR) ));
		factor(dType, new PLset(syncSet, FOLLOW(FACTOR)) );

		// Type Check, should be Boolean
		if ( dType != WordData::BOOL) {
			admin->errorNotBoolean(scanner->LineNum() );
			dType = WordData::ERROR;
		}

		admin->emit("NOT");

	}else	{
		admin->errorFactor(scanner->LineNum() );
		sync(syncSet);
	}



	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// VARIABLE_ACCESS
void Parser::variableAccess(Word* w, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "VARIABLE_ACCESS" << endl;
		indent+=tabStop;
	#endif
	Word* found;

	variableName(w, new PLset(syncSet, FOLLOW(VARIABLE_NAME)) );

	// Get the variable/constant
	if ( (found = symTable->find(w->Lex() )) == NULL ) {
		admin->errorUndefined(w->Lex(), scanner->LineNum() );

		//Add the value to avoid similar errors
		symTable->insert(w);
		w->setData(WordData::ERROR);
	}else {
		*w = *found;
	}

	// Constant access
	if ( ( w->Data() )->Constant() ) { 
		// NOTE: if w was an ID, then getNumericValue will change w to the found ID
		admin->emit("CONSTANT", getNumericValue(w) );

	// Variable access
	}else {
		admin->emit("VARIABLE", symTable->blockLevel() - w->Level(), w->Offset() );
	}

	indexedSelector(w,  new PLset(syncSet, FOLLOW(INDEXED_SELECTOR)) );

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// INDEXED_SELECTOR
void Parser::indexedSelector(Word *w, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "INDEXED_SELECTOR" << endl;
		indent+=tabStop;
	#endif
	int arraySize = 0;

	// if the current token is not in the first set, then skip
	if (!FIRST(INDEXED_SELECTOR)->has(curToken ) )	{
		indent.erase(0, tabStop.length() );
		return;
	}

	WordData::DataType dType = WordData::EMPTY;

	match(Token::LEFT_BRACKET, new PLset(syncSet, FIRST(EXPRESSION) ));
	expression(dType, new PLset(syncSet, FOLLOW(EXPRESSION)) );

	// Error if not Boolean or Integer
	if ( !(dType == WordData::BOOL || dType == WordData::NUMBER) ) {
		admin->errorInvalidValue(scanner->LineNum());
	}

	match(Token::RIGHT_BRACKET, syncSet);

	// Number Array
	if ( ( w->Data() )->Type() == WordData::NUMBER ) {
		arraySize = DATANUMBER(w->Data() )->Size();

	// Boolean Array
	}else if ( ( w->Data() )->Type() == WordData::BOOL ) {
		arraySize = DATABOOL( w->Data() )->Size();
	
	}else {
		// This should not happen, previous checks should catch this
		admin->errorInvalidValue(scanner->LineNum() );
	}
	admin->emit("INDEX", arraySize, scanner->LineNum() );


	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// CONSTANT
void Parser::constant(Word* w, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "CONSTANT" << endl;
		indent+=tabStop;
	#endif

	if (FIRST(NUMERAL)->has(curToken ) )	{
		numeral(w, new PLset(syncSet, FOLLOW(NUMERAL)) );

	}else if (FIRST(BOOLEAN_SYMBOL)->has(curToken ) )	{
		booleanSymbol(w,  new PLset(syncSet, FOLLOW(BOOLEAN_SYMBOL)) );

	}else if (FIRST(CONSTANT_NAME)->has(curToken ) )	{
		constantName(w, new PLset(syncSet, FOLLOW(CONSTANT_NAME)) );

	}else	{
		admin->errorConstant(scanner->LineNum() );
		sync(syncSet);
		indent.erase(0, tabStop.length() );
		return;
	}
	
	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// BOOLEAN_SYMBOL
void Parser::booleanSymbol(Word* w, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "BOOLEAN_SYMBOL" << endl;
		indent+=tabStop;
	#endif

	if (curToken->Type() == Token::KEYWORD && WORD(curToken)->Lex() == "false")	{
		if (w != NULL) {
			w->setData(false);
		}
		match(new Word("false", Token::KEYWORD) , syncSet);

	}else if (curToken->Type() == Token::KEYWORD && WORD(curToken)->Lex() == "true")	{

		if (w != NULL) {
			w->setData(true);
		}
		match(new Word("true", Token::KEYWORD) , syncSet);

	}else	{
		admin->errorNotBoolean(scanner->LineNum() );
		sync(syncSet);
	}

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// NUMERAL
void Parser::numeral(Word* w, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "NUMERAL" << endl;
		indent+=tabStop;
	#endif

	if (w != NULL)	{
		w->setData( NUMERIC(curToken)->Value() );
	}
	match(Token::NUMBER, syncSet);

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// CONSTANT_NAME
void  Parser::constantName(Word* w, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "CONSTANT_NAME" << endl;
		indent+=tabStop;
	#endif
		
	if (w != NULL && ( w->Lex() ).length() != 0 ) {
			Word* tmp = new Word();
			Word* found;

			matchID(tmp, syncSet);
			if ( (found = symTable->find(tmp->Lex() ) ) == NULL) {
				admin->errorUndefined(tmp->Lex(), scanner->LineNum() );

				//Add the value to avoid similar errors
				symTable->insert(tmp);
				w->setData(WordData::ERROR);

			}else{

				// Check if found is a constant
				if ( !( found->Data() )->Constant() ) { 
					admin->errorUndefined(found->Lex(), scanner->LineNum() );
				}
				w->setData(found->Lex() );
			}
	}else {
		matchID(w, syncSet);
	}

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// VARIABLE_NAME
void Parser::variableName(Word* w, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "VARIABLE_NAME" << endl;
		indent+=tabStop;
	#endif

	matchID(w, syncSet);

	indent.erase(0, tabStop.length() );
}

//-----------------------------------------------------------------------------
// PROCEDURE_NAME
void Parser::procedureName(Word* w, PLset* syncSet)	{
	#ifdef TEST_PARSER
		cout << indent << "PROCEDURE_NAME" << endl;
		indent+=tabStop;
	#endif
	matchID(w, syncSet);

	if (w != NULL) {
		w->setData(WordData::PROCEDURE);
	}

	indent.erase(0, tabStop.length() );
}
